/**
 * @file test_temp.cpp
 * @author Young_jam
 * @brief 使用VAO绘制一个四面体
 * @version 1.0
 * @date 2023/01/12
 */

/**
 * @name VBO
 * @brief
 * 用于在显存中提前开辟一块内存,用于缓存顶点数据,从而避免每次绘制都要在cpu和gpu之间拷贝数据,通过GL_ARRAY_BUFFER指定
 *
 * @name EBO
 * @brief
 * 与VBO功能相同,但是用于缓存图元索引(顶点序号)数据,通过GL_ELEMENT_ARRAY_BUFFER指定
 *
 * @name VAO
 * @brief
 * 顶点数组对象,用于管理VBO和EBO,减少glBindBuffer,glEnableVertexAttribArray,glVertexAttribPointer这些调用操作,高效地实现在顶点数组配置之间切换
 */
#include "c_defines.h"
#include "glut_win.h"
#include "shader_mgr.h"
#include <GL/glext.h>
#include <glm/ext.hpp>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>

using namespace std;

GLuint VAO = 0;
GLuint VBOs[2] = {0, 0};

void Init(void)
{
    glClearColor(.22f, .21f, .23f, 1.f);

    glEnable(GL_DEPTH_TEST);

    // 启用裁剪模式
    glEnable(GL_CULL_FACE);

    glPolygonMode(GL_FRONT, GL_LINE);  // 设置正面使用线条模式
    glPolygonMode(GL_BACK, GL_FILL);   // 设置背面使用填充模式
    // glCullFace(GL_BACK);              // 默认裁剪背面

    auto shader = CShaderManager::GetInstance().CreateNewShader("model");
    if (!shader->LoadShader("../shader/model.vert", "../shader/model.frag", 2, 0, "vVertexs", 1, "inColor"))
    {
        ERROR("load shader failed!");
        exit(EXIT_FAILURE);
    }

    GLfloat vertices[] = {10.f, 0.f, 10.f, 0.f, 0.f, -10.f, -10.f, 0.f, 0.f, 0.f, 10.f, 0.f};

    GLfloat colors[] = {1.0f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 1.f};

    // 逆时针为正面
    GLushort indices[] = {0, 1, 3, 1, 2, 3, 0, 3, 2, 0, 2, 1};

    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    glGenBuffers(2, VBOs);

    glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices) + sizeof(colors), nullptr, GL_STATIC_DRAW);
    // 顶点数组和颜色数组分别保存
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
    glBufferSubData(GL_ARRAY_BUFFER, sizeof(vertices), sizeof(colors), colors);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBOs[1]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void *)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void *)(sizeof(vertices)));
    glEnableVertexAttribArray(1);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    glBindVertexArray(0);
}

void Display()
{
    static auto shader = CShaderManager::GetInstance().GetShader("model");

    static glm::mat4 projection = glm::perspective(glm::radians(75.f), GLfloat(640) / GLfloat(480), 1.f, 100000.f);
    static glm::mat4 view = glm::lookAt(glm::vec3(0.f, 15.f, -20.f),     // 眼睛在哪里
                                        glm::vec3(0.f),                  // 眼睛看向哪里
                                        glm::vec3(0.f, 1000.f, -10.f));  // 头部指向哪里
    static glm::mat4 model = glm::scale(glm::mat4(1.f), glm::vec3(1.f));

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    shader->Enable();

    shader->SetUniformMatrix4fv("projection", glm::value_ptr(projection));
    shader->SetUniformMatrix4fv("view", glm::value_ptr(view));
    shader->SetUniformMatrix4fv("model", glm::value_ptr(model));

#if 1
    glBindVertexArray(VAO);
    // glDrawArrays(GL_TRIANGLES, 0, 4);    // !不能搭配EBO使用
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBOs[1]);
    glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_SHORT, (const GLvoid *)0);  // !需要搭配EBO使用
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    glBindVertexArray(0);
#else
    glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBOs[1]);
    glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_SHORT, (const GLvoid *)0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
#endif
    shader->Disable();

    glFinish();
}

int main(int argc, char **argv)
{
    CGlutWindow window("shader test");

    window.Init(&argc, argv);

    Init();

    window.RegisterCallbackFunc([]() {
        glutDisplayFunc(Display);
        glutIdleFunc(Display);
    });

    window.MainLoop();

    return 0;
}